<!DOCTYPE html>
<html>
<!--
Copyright 2007 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>MP Unit Tests - mp.tools.Matrix</title>
<script src="/js/vendor/closure-library/closure/goog/base.js"></script>
<script>
    goog.require('goog.debug.Logger');
    goog.require('goog.math.Coordinate');
    goog.require('goog.math.Matrix');
    goog.require('goog.object');
    goog.require('goog.structs.Map');
    goog.require('goog.testing.jsunit');
</script>
<script src="/js/mp/tools/matrix.js"></script>
</head>
<body>
<pre>
<script>
    function testConstuctorWithGoodArray() {
        var a1 = [[1, 2], [2, 3], [4, 5]];
        var m1 = new mp.tools.Matrix(a1);
        assertArrayEquals('1. Internal array should be the same', m1.toArray(), a1);
        assertEquals(3, m1.getSize().height);
        assertEquals(2, m1.getSize().width);
        var a2 = [[-61, 45, 123], [11112, 343, 1235]];
        var m2 = new mp.tools.Matrix(a2);
        assertArrayEquals('2. Internal array should be the same', m2.toArray(), a2);
        assertEquals(2, m2.getSize().height);
        assertEquals(3, m2.getSize().width);
        var a3 = [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
        var m3 = new mp.tools.Matrix(a3);
        assertArrayEquals('3. Internal array should be the same', m3.toArray(), a3);
        assertEquals(4, m3.getSize().height);
        assertEquals(4, m3.getSize().width);
    }

    function testConstructorWithBadArray() {
        assertThrows('1. All arrays should be of equal length', function () {
            new mp.tools.Matrix([[1, 2, 3], [1, 2], [1]]);
        });
        assertThrows('2. All arrays should be of equal length', function () {
            new mp.tools.Matrix([[1, 2], [1, 2], [1, 2, 3, 4]]);
        });
        assertThrows('3. Arrays should contain only numeric values', function () {
            new mp.tools.Matrix([[1, 2], [1, 2], [1, 'a']]);
        });
        assertThrows('4. Arrays should contain only numeric values', function () {
            new mp.tools.Matrix([[1, 2], [1, 2], [1, { a: 3}]]);
        });
        assertThrows('5. Arrays should contain only numeric values', function () {
            new mp.tools.Matrix([[1, 2], [1, 2], [1, [1, 2, 3]]]);
        });
    }

    function testConstructorWithGoodNumbers() {
        var m1 = new mp.tools.Matrix(2, 2);
        assertEquals('Height should be 2', 2, m1.getSize().height);
        assertEquals('Width should be 2', 2, m1.getSize().width);
        var m2 = new mp.tools.Matrix(4, 2);
        assertEquals('Height should be 4', 4, m2.getSize().height);
        assertEquals('Width should be 2', 2, m2.getSize().width);
        var m3 = new mp.tools.Matrix(4, 6);
        assertEquals('Height should be 4', 4, m3.getSize().height);
        assertEquals('Width should be 6', 6, m3.getSize().width);
    }

    function testConstructorWithBadNumbers() {
        assertThrows('1. Negative argument should have errored', function () {
            new mp.tools.Matrix(-4, 6);
        });
        assertThrows('2. Negative argument should have errored', function () {
            new mp.tools.Matrix(4, -6);
        });
        assertThrows('3. Zero argument should have errored', function () {
            new mp.tools.Matrix(4, 0);
        });
        assertThrows('4. Zero argument should have errored', function () {
            new mp.tools.Matrix(0, 1);
        });
    }

    function testConstructorWithMatrix() {
        var a1 = [[1, 2], [2, 3], [4, 5]];
        var m1 = new mp.tools.Matrix(a1);
        var m2 = new mp.tools.Matrix(m1);
        assertArrayEquals(
          'Internal arrays should be the same', m1.toArray(), m2.toArray());
        assertNotEquals(
          'Should be different objects', goog.getUid(m1), goog.getUid(m2));
    }

    function testSetValueAt() {
        var m = new mp.tools.Matrix(3, 3);
        for (var x = 0; x < 3; x++) {
            for (var y = 0; y < 3; y++) {
                m.setValueAt(x, y, 3 * x - y);
            }
        }
        assertArrayEquals([[0, -1, -2], [3, 2, 1], [6, 5, 4]], m.toArray());
    }

    function testGetValueAt() {
        var m = new mp.tools.Matrix([[0, -1, -2], [3, 2, 1], [6, 5, 4]]);
        for (var x = 0; x < 3; x++) {
            for (var y = 0; y < 3; y++) {
                assertEquals(
              'Value at (x, y) should equal 3x - y',
              3 * x - y, m.getValueAt(x, y));
            }
        }
        assertNull('Out of bounds value should be null', m.getValueAt(-1, 2));
        assertNull('Out of bounds value should be null', m.getValueAt(-1, 0));
        assertNull('Out of bounds value should be null', m.getValueAt(0, 4));
    }

    function testSum1() {
        var m1 = new mp.tools.Matrix([[1, 1, 1], [2, 2, 2], [3, 3, 3]]);
        var m2 = new mp.tools.Matrix([[3, 3, 3], [2, 2, 2], [1, 1, 1]]);
        assertArrayEquals('Sum should be all the 4s',
          [[4, 4, 4], [4, 4, 4], [4, 4, 4]], m1.add(m2).toArray());
        assertArrayEquals('Addition should be commutative',
          m1.add(m2).toArray(), m2.add(m1).toArray());
    }

    function testSum2() {
        var m1 = new mp.tools.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
        var m2 = new mp.tools.Matrix([[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]);
        assertArrayEquals('Sum should be all 0s',
          [[0, 0, 0], [0, 0, 0], [0, 0, 0]], m1.add(m2).toArray());
        assertArrayEquals('Addition should be commutative',
          m1.add(m2).toArray(), m2.add(m1).toArray());
    }

    function testSubtract1() {
        var m1 = new mp.tools.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
        var m2 = new mp.tools.Matrix([[5, 5, 5], [5, 5, 5], [5, 5, 5]]);
        assertArrayEquals([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]],
                        m1.subtract(m2).toArray());
        assertArrayEquals([[4, 3, 2], [1, 0, -1], [-2, -3, -4]],
                        m2.subtract(m1).toArray());
    }

    function testSubtract2() {
        var m1 = new mp.tools.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
        var m2 = new mp.tools.Matrix([[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]);
        assertArrayEquals([[2, 4, 6], [8, 10, 12], [14, 16, 18]],
                        m1.subtract(m2).toArray());
        assertArrayEquals([[-2, -4, -6], [-8, -10, -12], [-14, -16, -18]],
                        m2.subtract(m1).toArray());
    }

    function testScalarMultiplication() {
        var m1 = new mp.tools.Matrix([[1, 1, 1], [2, 2, 2], [3, 3, 3]]);
        assertArrayEquals(
          [[2, 2, 2], [4, 4, 4], [6, 6, 6]], m1.multiply(2).toArray());
        assertArrayEquals(
          [[3, 3, 3], [6, 6, 6], [9, 9, 9]], m1.multiply(3).toArray());
        assertArrayEquals(
          [[4, 4, 4], [8, 8, 8], [12, 12, 12]], m1.multiply(4).toArray());
        var m2 = new mp.tools.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
        assertArrayEquals(
          [[2, 4, 6], [8, 10, 12], [14, 16, 18]], m2.multiply(2).toArray());
    }

    function testMatrixMultiplication() {
        var m1 = new mp.tools.Matrix([[1, 2], [3, 4]]);
        var m2 = new mp.tools.Matrix([[3, 4], [5, 6]]);
        // m1 * m2
        assertArrayEquals([[1 * 3 + 2 * 5, 1 * 4 + 2 * 6],
                         [3 * 3 + 4 * 5, 3 * 4 + 4 * 6]],
                        m1.multiply(m2).toArray());
        // m2 * m1 != m1 * m2
        assertArrayEquals([[3 * 1 + 4 * 3, 3 * 2 + 4 * 4],
                         [5 * 1 + 6 * 3, 5 * 2 + 6 * 4]],
                        m2.multiply(m1).toArray());
        var m3 = new mp.tools.Matrix([[1, 2, 3, 4],
                                     [5, 6, 7, 8]]);
        var m4 = new mp.tools.Matrix([[1, 2, 3],
                                     [4, 5, 6],
                                     [7, 8, 9],
                                     [10, 11, 12]]);
        // m3 * m4
        assertArrayEquals([[1 * 1 + 2 * 4 + 3 * 7 + 4 * 10,
                          1 * 2 + 2 * 5 + 3 * 8 + 4 * 11,
                          1 * 3 + 2 * 6 + 3 * 9 + 4 * 12],
                         [5 * 1 + 6 * 4 + 7 * 7 + 8 * 10,
                          5 * 2 + 6 * 5 + 7 * 8 + 8 * 11,
                          5 * 3 + 6 * 6 + 7 * 9 + 8 * 12]],
                        m3.multiply(m4).toArray());
        assertThrows("Matrix dimensions should not line up.",
                   function () { m4.multiply(m3); });
    }
    function testMatrixMultiplicationIsAssociative() {
        var A = new mp.tools.Matrix([[1, 2], [3, 4]]);
        var B = new mp.tools.Matrix([[3, 4], [5, 6]]);
        var C = new mp.tools.Matrix([[2, 7], [9, 1]]);
        assertArrayEquals('A(BC) == (AB)C',
                        A.multiply(B.multiply(C)).toArray(),
                        A.multiply(B).multiply(C).toArray());
    }

    function testMatrixMultiplicationIsDistributive() {
        var A = new mp.tools.Matrix([[1, 2], [3, 4]]);
        var B = new mp.tools.Matrix([[3, 4], [5, 6]]);
        var C = new mp.tools.Matrix([[2, 7], [9, 1]]);
        assertArrayEquals('A(B + C) = AB + AC',
                        A.multiply(B.add(C)).toArray(),
                        A.multiply(B).add(A.multiply(C)).toArray());
        assertArrayEquals('(A + B)C = AC + BC',
                        A.add(B).multiply(C).toArray(),
                        A.multiply(C).add(B.multiply(C)).toArray());
    }

    function testTranspose() {
        var m = new mp.tools.Matrix([[1, 3, 1], [0, -6, 0]]);
        var t = [[1, 0], [3, -6], [1, 0]];
        assertArrayEquals(t, m.getTranspose().toArray());
    }

    function testAppendColumns() {
        var m = new mp.tools.Matrix([[1, 3, 2], [2, 0, 1], [5, 2, 2]]);
        var b = new mp.tools.Matrix([[4], [3], [1]]);
        var result = [[1, 3, 2, 4], [2, 0, 1, 3], [5, 2, 2, 1]];
        assertArrayEquals(result, m.appendColumns(b).toArray());
    }

    function testAppendRows() {
        var m = new mp.tools.Matrix([[1, 3, 2], [2, 0, 1], [5, 2, 2]]);
        var b = new mp.tools.Matrix([[4, 3, 1]]);
        var result = [[1, 3, 2], [2, 0, 1], [5, 2, 2], [4, 3, 1]];
        assertArrayEquals(result, m.appendRows(b).toArray());
    }

    function testSubmatrixByDeletion() {
        var m = new mp.tools.Matrix([[1, 2, 3, 4], [5, 6, 7, 8],
          [9, 10, 11, 12], [13, 14, 15, 16]]);
        var arr = [[1, 2, 3], [5, 6, 7], [13, 14, 15]];
        assertArrayEquals(arr, m.getSubmatrixByDeletion_(2, 3).toArray());
    }

    function testMinor() {
        var m = new mp.tools.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
        assertEquals(-3, m.getMinor_(0, 0));
    }

    function testCofactor() {
        var m = new mp.tools.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
        assertEquals(6, m.getCofactor_(0, 1));
    }

    function testDeterminantForOneByOneMatrix() {
        var m = new mp.tools.Matrix([[3]]);
        assertEquals(3, m.getDeterminant());
    }

    function testDeterminant() {
        var m = new mp.tools.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
        assertEquals(0, m.getDeterminant());
    }

    function testGetSubmatrix() {
        var m = new mp.tools.Matrix([[2, -1, 0, 1, 0, 0],
                                    [-1, 2, -1, 0, 1, 0],
                                    [0, -1, 2, 0, 0, 1]]);
        var sub1 = [[2, -1, 0], [-1, 2, -1], [0, -1, 2]];
        assertArrayEquals(sub1,
                        m.getSubmatrixByCoordinates_(0, 0, 2, 2).toArray());
        var sub2 = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
        assertArrayEquals(sub2, m.getSubmatrixByCoordinates_(0, 3).toArray());
    }

    function testGetReducedRowEchelonForm() {
        var m = new mp.tools.Matrix([[2, -1, 0, 1, 0, 0],
                                    [-1, 2, -1, 0, 1, 0],
                                    [0, -1, 2, 0, 0, 1]]);
        var expected = new mp.tools.Matrix([[1, 0, 0, .75, .5, .25],
                                           [0, 1, 0, .5, 1, .5],
                                           [0, 0, 1, .25, .5, .75]]);
        assertTrue(expected.equals(m.getReducedRowEchelonForm()));
    }

    function testInverse() {
        var m1 = new mp.tools.Matrix([[2, -1, 0],
                                     [-1, 2, -1],
                                     [0, -1, 2]]);
        var expected1 = new mp.tools.Matrix([[.75, .5, .25],
                                            [.5, 1, .5],
                                            [.25, .5, .75]]);
        assertTrue(expected1.equals(m1.getInverse()));
        var m2 = new mp.tools.Matrix([[4, 8],
                                     [7, -2]]);
        var expected2 = new mp.tools.Matrix([[.03125, .125],
                                            [.10936, -.0625]]);
        assertTrue(expected2.equals(m2.getInverse(), .0001));
    }

    function testEquals() {
        var a1 = new mp.tools.Matrix([[1, 0, 0, .75, .5, .25],
                                     [0, 1, 0, .5, 1, .5],
                                     [0, 0, 1, .25, .5, .75]]);
        var a2 = new mp.tools.Matrix([[1, 0, 0, .75, .5, .25],
                                     [0, 1, 0, .5, 1, .5],
                                     [0, 0, 1, .25, .5, .75]]);
        var a3 = new mp.tools.Matrix([[1, 0, 0, .749, .5, .25],
                                     [0, 1, 0, .5, 1, .5],
                                     [0, 0, 1, .25, .5, .75]]);
        assertTrue(a1.equals(a2));
        assertTrue(a1.equals(a3, .01));
        assertFalse(a1.equals(a3, .001));
    }

    function testMakeSquare() {

        var a1 = [[1, 2, 0, 0, .75, .5, .25],
                    [],
                    [0, 1, 0, .5, 1, .5],
                    [0, 0, 1, .25, .5, .75]];

        mp.tools.Matrix.makeSquare(a1);

        assertEquals(a1[0].length, a1[1].length);
        assertEquals(a1[0].length, a1[2].length);
        assertEquals(a1[0].length, a1[3].length);
    }

    function testClone() {

        var a1 = [[1, 2, 3],
                    [1, 2, 3],
                    [1, 2, 3]];

        var m1 = new mp.tools.Matrix(a1);

        var m2 = m1.clone();

        assertTrue(m1.equals(m2));

        m1.array_[0][0] = 0;

        assertFalse(m1.equals(m2));
    }

    function testWidthAndHeight() {

        var a1 = [[1, 2, 3, 4],
                    [1, 2, 3, 4],
                    [1, 2, 3, 4]];

        var m1 = new mp.tools.Matrix(a1);

        assertEquals(a1.length, m1.height());
        assertEquals(a1[0].length, m1.width());
    }

    function testLine() {

        var a1 = [[1, 2, 3, 4],
                    [1, 2, 3, 4],
                    [1, 2, 3, 4]];

        var m1 = new mp.tools.Matrix(a1);

        assertTrue(goog.array.equals(m1.line(0), a1[0]));
        assertTrue(goog.array.equals(m1.line(2, true), [3, 3, 3]));
    }

    function testInsertLine() {

        var a1 = [[1, 2, 3, 4],
                    [1, 2, 3, 4],
                    [1, 2, 3, 4]];

        var m1 = new mp.tools.Matrix(a1);

        var m2 = m1.insertLine(2, [5, 5, 5], true);

        var m3 = m2.insertLine(3, [1, 1, 1, 1, 1]);

        var m4 = new mp.tools.Matrix([[1, 2, 5, 3, 4],
                                [1, 2, 5, 3, 4],
                                [1, 2, 5, 3, 4],
                                [1, 1, 1, 1, 1]]);
        assertFalse(m1.equals(m2));
        assertFalse(m2.equals(m3));
        assertTrue(m3.equals(m4));
    }

    function testRemoveLine() {

        var a1 = [[1, 2, 5, 3, 4],
                                [1, 2, 5, 3, 4],
                                [1, 2, 5, 3, 4],
                                [1, 1, 1, 1, 1]];
        var a2 = [[1, 2, 3, 4],
                    [1, 2, 3, 4],
                    [1, 2, 3, 4]];

        var m1 = new mp.tools.Matrix(a1);

        var m2 = m1.removeLine(2, true);

        var m3 = m2.removeLine(3);

        var m4 = new mp.tools.Matrix(a2);

        assertFalse(m1.equals(m2));
        assertFalse(m2.equals(m3));
        assertTrue(m3.equals(m4));
    }

    function testRotate() {
        var a1 = [[1, 2, 3, 4],
                    [1, 2, 3, 4],
                    [1, 2, 3, 4]];
                    
        var a2 = [[1, 1, 1],
                    [2, 2, 2],
                    [3, 3, 3],
                    [4, 4, 4]];

        var m1 = new mp.tools.Matrix(a1);
        var m2 = m1.rotate();
        var m3 = new mp.tools.Matrix(a2);
        
        assertTrue(m2.equals(m3));
    }

    function testReflectNonEmpty() {
        var a1 = [  [2, 0, 2, 0, 2, 0],
                    [0, 2, 3, 4, 5, 0],
                    [0, 3, 4, 5, 6, 0],
                    [2, 4, 5, 6, 7, 0],
                    [2, 0, 0, 0, 0, 0]];
                    
        var a2 = [  [2, 0, 2, 0, 2, 2],
                    [0, 2, 3, 4, 5, 0],
                    [0, 3, 4, 5, 6, 0],
                    [2, 4, 5, 6, 7, 2],
                    [2, 0, 2, 0, 2, 2]];

        var m1 = new mp.tools.Matrix(a1);
        m1.reflectNonEmpty(2);
        var m2 = new mp.tools.Matrix(a2);
        
        assertTrue(m2.equals(m1));
    }



    function testGetNonEmptyEdgeIdxs() {
        var a1 = [  [2, 0, 2, 0, 2, 0],
                    [0, 2, 3, 4, 5, 0],
                    [0, 3, 4, 5, 6, 0],
                    [2, 4, 5, 6, 7, 0],
                    [2, 0, 0, 0, 0, 0]];
                    
        var a2 = [  [0,2,4],
                    [0,3,4] ];

        var m1 = new mp.tools.Matrix(a1);
        var a3 = m1.getNonEmptyEdgeIdxs(2, false);
        var a4 = m1.getNonEmptyEdgeIdxs(2, true);

        assertArrayEquals('1. Line array should be the same', a2[0], a3);
        assertArrayEquals('2. Line array should be the same', a2[1], a4);
    }

    function testGetValueIdxsMap() {
        var a1 = [  [2, 0, 2, 0, 2, 0],
                    [0, 2, 3, 4, 5, 0],
                    [0, 3, 4, 5, 6, 0],
                    [2, 4, 5, 6, 7, 0],
                    [2, 0, 0, 0, 0, 0]];
                    
        var map1 = new goog.structs.Map();
        map1.set(4, [
            new goog.math.Coordinate(3, 1),
            new goog.math.Coordinate(2, 2),
            new goog.math.Coordinate(1, 3)]);

        var m1 = new mp.tools.Matrix(a1);
        var map2 = m1.getValueIdxsMap();

        assertEquals(map2.getCount(), 7);

        assertArrayEquals('Array should be the same', map1.get(4), map2.get(4));
    }

    function testGetNeighbours() {
        var a1 = [  [2, 0, 2, 0, 2, 0],
                    [0, 2, 3, 4, 5, 0],
                    [0, 3, 4, 5, 6, 0],
                    [2, 4, 5, 6, 7, 0],
                    [2, 0, 0, 0, 0, 0]];
                    
        var m1 = new mp.tools.Matrix(a1);
        var vals1 = m1.getNeighbours(0, 5);
        var vals2 = m1.getNeighbours(1, 1, true);
        var vals3 = m1.getNeighbours(4, 2);

        assertEquals(vals1.length, 2);
        assertEquals(vals2.length, 8);
        assertEquals(vals3.length, 3);
        
        goog.array.sort(vals1);
        goog.array.sort(vals2);
        goog.array.sort(vals3);

        assertArrayEquals('Array should be the same', vals1, [0,2]);
        assertArrayEquals('Array should be the same', vals2, [0,0,0,2,2,3,3,4]);
        assertArrayEquals('Array should be the same', vals3, [0,0,5]);
    }
</script>
</pre>
</body>
</html>
